﻿// ****************************************************************************
// <copyright file="EventToCommand.cs" company="GalaSoft Laurent Bugnion">
// Copyright © GalaSoft Laurent Bugnion 2009-2012
// </copyright>
// ****************************************************************************
// <author>Laurent Bugnion</author>
// <email>laurent@galasoft.ch</email>
// <date>3.11.2009</date>
// <project>GalaSoft.MvvmLight.Extras</project>
// <web>http://www.galasoft.ch</web>
// <license>
// See license.txt in this solution or http://www.galasoft.ch/license_MIT.txt
// </license>
// <LastBaseLevel>BL0004</LastBaseLevel>
// ****************************************************************************
using System;
using System.Windows;
using System.Windows.Input;
using System.Windows.Interactivity;

namespace TimersXP.CommandBase
{
    /// <summary>
    /// This <see cref="System.Windows.Interactivity.TiggerAction"/> can be used to bind any event on any FrameworkElement to any <see cref="ICommand"/>.
    /// Typically, this element is used in XAML to connect the attached element to a command located in a ViewModel. This trigger can only be attached to a FrameworkElement or a class
    /// deriving from FrameworkElement. <para>To access the EventArgs of the fired event, use a RelayCommand&lt;EventArgs&gt; and leave the CommandParameter and CommandParameterValue empty!</para>
    /// </summary>
    /// ////[ClassInfo(typeof(EventToCommand),
    ////  VersionString = "4.2.7",
    ////  DateString = "201309262235",
    ////  Description = "A Trigger used to bind any event to an ICommand.",
    ////  UrlContacts = "http://www.galasoft.ch/contact_en.html",
    ////  Email = "laurent@galasoft.ch")]
    public partial class EventToCommand : TriggerAction<DependencyObject>
    {
        /// <summary>
        /// Gets or sets a value indicating whether the EventArgs passed to the event handler will be forwarded to the ICommand's Execute method when the event is fired
        /// (if the bound ICommand accepts an argument of type EventArgs). <para>For example, use a RelayCommand&ltMouseEventArgs&gt; to get the arguments of a MouseMove event.</para>
        /// </summary>
        /// <value><c>true</c> if [pass event arguments to command]; otherwise, <c>false</c>.</value>
        public bool PassEventArgsToCommand { get; set; }

        /// <summary>Gets or sets the converter used to convert the EventArgs when using <see cref="PassEventArgsToCommand"/> If PassEventArgsToCommand is false, this property is never used.</summary>
        /// <value>The event arguments converter.</value>
        public IEventArgsConverter EventArgsConverter { get; set; }

        /// <summary>The <see cref="EventArgsConverterParameter"/> dependency property's name.</summary>
        public const string EventArgsConverterParameterPropertyName = "EventArgsConverterParameter";

        /// <summary>
        /// Gets or sets a parameters for the converter used to convert the EventArgs when using <see cref="PassEventArgsToCommand"/>.
        /// If PassEventArgsToCommand is false, this property is never used. This is a dependency property.
        /// </summary>
        /// <value>The event arguments converter parameter.</value>
        public object EventArgsConverterParameter
        {
            get { return GetValue(EventArgsConverterParameterProperty); }
            set { SetValue(EventArgsConverterParameterProperty, value); }
        }

        /// <summary>Identifies the <see cref="EventArgsConverterParameter"/> dependency property.</summary>
        public static readonly DependencyProperty EventArgsConverterParameterProperty = DependencyProperty.Register(EventArgsConverterParameterPropertyName, 
            typeof(object), typeof(EventToCommand), new UIPropertyMetadata(null));

        /// <summary>Provides a simple way to invoke this trigger programatically without any EventArgs.</summary>
        public void Invoke() { Invoke(null); }

        /// <summary>Executes the trigger. <para>To access the EventArgs of the fired event, use a RelayCommand&lt;EventArgs&gt; and leave the CommandParameter and CommandParameterValue empty</para></summary>
        /// <param name="parameter">The EventArgs of the fired event.</param>
        protected override void Invoke(object parameter)
        {
            if (AssociatedElementIsDisabled())
                return;

            var command = GetCommand();
            var commandParameter = CommandParameterValue;

            if (commandParameter == null && PassEventArgsToCommand)
                commandParameter = EventArgsConverter == null ? parameter : EventArgsConverter.Convert(parameter, EventArgsConverterParameter);

            if (command != null && command.CanExecute(commandParameter))
                command.Execute(commandParameter);
        }

        /// <summary>Called when [command changed].</summary>
        /// <param name="element">The element.</param>
        /// <param name="e">The <see cref="DependencyPropertyChangedEventArgs"/> instance containing the event data.</param>
        private static void OnCommandChanged(EventToCommand element, DependencyPropertyChangedEventArgs e)
        {
            if (element == null)
                return;

            if (e.OldValue != null)
                ((ICommand)e.OldValue).CanExecuteChanged -= element.OnCommandCanExecuteChanged;

            var command = (ICommand)e.NewValue;
            if (command != null)
                command.CanExecuteChanged += element.OnCommandCanExecuteChanged;

            element.EnableDisableElement();
        }

        /// <summary>Associateds the element is disabled.</summary>
        /// <returns></returns>
        private bool AssociatedElementIsDisabled()
        {
            var element = GetAssociatedObject();
            return AssociatedObject == null || (element != null && !element.IsEnabled);
        }

        /// <summary>Enables the disable element.</summary>
        private void EnableDisableElement()
        {
            var element = GetAssociatedObject();
            if (element == null)
                return;

            var command = GetCommand();
            if (MustToggleIsEnabledValue && command != null)
                element.IsEnabled = command.CanExecute(CommandParameterValue);
        }

        /// <summary>Called when [command can execute changed].</summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="EventArgs"/> instance containing the event data.</param>
        private void OnCommandCanExecuteChanged(object sender, EventArgs e) { EnableDisableElement(); }
    }
}
